#include <defs.h>
#include <stdio.h>
#include <string.h>
#include <console.h>
#include <kdebug.h>
#include <picirq.h>
#include <trap.h>
#include <clock.h>
#include <intr.h>
#include <pmm.h>
#include <vmm.h>
#include <ide.h>
#include <swap.h>
#include <kmonitor.h>

int kern_init(void) __attribute__((noreturn));
void grade_backtrace(void);
static void lab1_switch_test(void);

int
kern_init(void) {
    extern char edata[], end[]; //声明外部变量 edata 和 end
    memset(edata, 0, end - edata); // 将数据段清零

    cons_init();                // init the console 初始化控制台

    const char *message = "(THU.CST) os is loading ...";
    cprintf("%s\n\n", message);// 将消息输出到控制台

    print_kerninfo();// 输出内核信息的函数

    grade_backtrace(); //调用回溯函数，通常用于调试，显示函数调用栈。

    pmm_init();                 // init physical memory management初始化物理内存管理

    pic_init();                 // init interrupt controller初始化可编程中断控制器
    idt_init();                 // init interrupt descriptor table初始化中断描述符表

    vmm_init();                 // init virtual memory management 初始化虚拟内存管理

    ide_init();                 // init ide devices初始化IDE设备
    swap_init();                // init swap 初始化交换分区

    clock_init();               // init clock interrupt 初始化时钟中断
    intr_enable();              // enable irq interrupt启用中断

    //LAB1: CAHLLENGE 1 If you try to do it, uncomment lab1_switch_test()
    // user/kernel mode switch test
    lab1_switch_test();

    /* do nothing */
    while (1);
}

//不进行内联的回溯函数，调用 mon_backtrace 显示当前的调用栈。
void __attribute__((noinline))
grade_backtrace2(int arg0, int arg1, int arg2, int arg3) {
    mon_backtrace(0, NULL, NULL);
}
//不进行内联的回溯函数，传递参数到 grade_backtrace2
void __attribute__((noinline))
grade_backtrace1(int arg0, int arg1) {
    grade_backtrace2(arg0, (int)&arg0, arg1, (int)&arg1);
}
//不进行内联的回溯函数，传递参数到 grade_backtrace1
void __attribute__((noinline))
grade_backtrace0(int arg0, int arg1, int arg2) {
    grade_backtrace1(arg0, arg2);
}
//触发回溯的起始点，传递初始化函数地址。
void
grade_backtrace(void) {
    grade_backtrace0(0, (int)kern_init, 0xffff0000);
}
//打印当前的段寄存器状态。
static void
lab1_print_cur_status(void) {
    static int round = 0;
    uint16_t reg1, reg2, reg3, reg4;
    //嵌入汇编代码，确保编译器不优化这些代码。
    asm volatile (
            "mov %%cs, %0;"// 将当前代码段寄存器的值移动到 reg1
            "mov %%ds, %1;"
            "mov %%es, %2;"
            "mov %%ss, %3;"
            : "=m"(reg1), "=m"(reg2), "=m"(reg3), "=m"(reg4));
    cprintf("%d: @ring %d\n", round, reg1 & 3);//打印当前的 round、权限级（ring）和各段寄存器的值。
    cprintf("%d:  cs = %x\n", round, reg1);
    cprintf("%d:  ds = %x\n", round, reg2);
    cprintf("%d:  es = %x\n", round, reg3);
    cprintf("%d:  ss = %x\n", round, reg4);
    round ++;//将 round 增加1，以便每次调用时记录状态。
}

static void
lab1_switch_to_user(void) {
    // 从内核模式切换到用户模式
    //LAB1 CHALLENGE 1 : TODO
    asm volatile (
	    "sub $0x8, %%esp \n"//从堆栈指针中减去 8 字节
	    "int %0 \n"//通过触发一个中断，将控制权转移到内核，切换到用户模式。
	    "movl %%ebp, %%esp"// 将基指针（EBP）值移动到堆栈指针（ESP），恢复堆栈指针。
	    : 
	    : "i"(T_SWITCH_TOU)//T_SWITCH_TOU是一个常量，表示切换到用户态的中断号。传入常量 T_SWITCH_TOU
	);
}

static void
lab1_switch_to_kernel(void) {
    // 从用户模式切换到内核模式
    //LAB1 CHALLENGE 1 :  TODO
    asm volatile (
	    "int %0 \n"// 同样触发中断，这里用的是 T_SWITCH_TOK，从用户态切换回内核态。
	    "movl %%ebp, %%esp \n"//恢复堆栈指针
	    : 
	    : "i"(T_SWITCH_TOK)//传入常量 T_SWITCH_TOU
	);
}

//测试用户模式和内核模式切换。
//调用 lab1_print_cur_status 打印当前状态，进行模式切换，然后再次打印状态。
static void
lab1_switch_test(void) {
    lab1_print_cur_status();
    cprintf("+++ switch to  user  mode +++\n");
    lab1_switch_to_user();
    lab1_print_cur_status();
    cprintf("+++ switch to kernel mode +++\n");
    lab1_switch_to_kernel();
    lab1_print_cur_status();
}

